package cool.taomu.toolkit.httpclient

import com.google.gson.Gson
import cool.taomu.toolkit.annotations.httpclient.HttpClient
import cool.taomu.toolkit.utils.UserAgentUtils
import java.lang.reflect.Method
import java.util.HashMap
import javax.ws.rs.Consumes
import javax.ws.rs.CookieParam
import javax.ws.rs.DELETE
import javax.ws.rs.GET
import javax.ws.rs.HeaderParam
import javax.ws.rs.POST
import javax.ws.rs.PUT
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.MediaType
import org.apache.hc.client5.http.classic.methods.HttpDelete
import org.apache.hc.client5.http.classic.methods.HttpGet
import org.apache.hc.client5.http.classic.methods.HttpPost
import org.apache.hc.client5.http.classic.methods.HttpPut
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.core5.http.ContentType
import org.apache.hc.core5.http.HttpHeaders
import org.apache.hc.core5.http.io.entity.EntityUtils
import org.apache.hc.core5.http.io.entity.StringEntity

class ApacheHttpInvoker {

    static final CloseableHttpClient CLIENT = HttpClients.createDefault()

    def static Object invoke(Class<?> zlass, Method method, Object[] args) throws Exception {
        val declaringClass = zlass
        // 检查是否标注了 @HttpClient
        if (!declaringClass.isAnnotationPresent(HttpClient)) {
            throw new IllegalStateException('''接口 «declaringClass.name» 未标注 @HttpClient，请先添加注解''')
        }
        val httpClientAnno = declaringClass.getAnnotation(HttpClient)
        val baseUrl = httpClientAnno.value
        // 构建完整 URL
        val classPath = declaringClass.getAnnotation(Path)
        val methodPath = method.getAnnotation(Path)

        var cpath = classPath !== null ? !classPath.value.startsWith("/") ? #["/", classPath.value].
                join("") : classPath.value : ""
        var mpath = methodPath !== null ? !methodPath.value.startsWith("/") ? #["/", methodPath.value].
                join("") : methodPath.value : ""

        var fullPath = baseUrl + cpath + mpath

        // 构建完整 URL
        var gson = new Gson();
        // 存储查询参数
        var queryParams = new HashMap<String, String>()
        var HttpUriRequestBase request;
        // 替换路径参数 {id} -> 参数值
        for (var i = 0; i < args.length; i++) {
            var param = method.parameters.get(i)
            if (param.isAnnotationPresent(PathParam)) {
                var pathParam = param.getAnnotation(PathParam)
                var paramName = pathParam.value
                fullPath = fullPath.replace("{" + paramName + "}", args.get(i).toString)
            } else if (param.isAnnotationPresent(QueryParam)) {
                var queryParam = param.getAnnotation(QueryParam)
                var paramName = queryParam.value
                queryParams.put(paramName, args.get(i).toString)
            } else if (param.isAnnotationPresent(HeaderParam)) {
                var headerParam = param.getAnnotation(HeaderParam)
                var paramName = headerParam.value
                request.setHeader(paramName, args.get(i).toString)
            } else if (param.isAnnotationPresent(CookieParam)) {
                var cookieParam = param.getAnnotation(CookieParam)
                var paramName = cookieParam.value
                request.setHeader(HttpHeaders.COOKIE, paramName + "=" + args.get(i).toString)
            }
        }

        // 拼接查询参数到 URL
        if (!queryParams.isEmpty) {
            val joins = newArrayList();
            queryParams.forEach [ k, v |
                joins.add(#[k, v].join("="));
            ]
            fullPath = fullPath + "?" + joins.join("&")
        }

        // 创建请求对象
        if (method.isAnnotationPresent(GET)) {
            request = new HttpGet(fullPath)
        } else if (method.isAnnotationPresent(POST)) {
            var post = new HttpPost(fullPath)
            var body = findRequestBody(method, args)
            var json = gson.toJson(body)
            var entity = new StringEntity(json, ContentType.APPLICATION_JSON)
            post.setEntity(entity)
            request = post
        } else if (method.isAnnotationPresent(PUT)) {
            var put = new HttpPut(fullPath)
            var body = findRequestBody(method, args)
            var json = gson.toJson(body)
            var entity = new StringEntity(json, ContentType.APPLICATION_JSON)
            put.setEntity(entity)
            request = put
        } else if (method.isAnnotationPresent(DELETE)) {
            request = new HttpDelete(fullPath)
        } else {
            throw new UnsupportedOperationException("不支持的 HTTP 方法")
        }

        // 设置 Accept / Content-Type
        if (method.isAnnotationPresent(Produces)) {
            request.setHeader("Accept", MediaType.APPLICATION_JSON)
        }
        if (method.isAnnotationPresent(Consumes)) {
            request.setHeader("Content-Type", MediaType.APPLICATION_JSON)
        }

        // 设置 User-Agent
        request.setHeader("User-Agent", UserAgentUtils.getDefaultUserAgent())

        // 执行请求
        var response = CLIENT.executeOpen(null, request, null)
        var responseBody = EntityUtils.toString(response.getEntity)

        if (response.code == 200 && responseBody !== null && !responseBody.isEmpty &&
            response.getHeader("Content-Type").equals(MediaType.APPLICATION_JSON)) {
            return gson.fromJson(responseBody, method.returnType)
        } else {
            return responseBody
        }
    }

    private static def Object findRequestBody(Method method, Object[] args) {
        for (var i = 0; i < method.parameterCount; i++) {
            var param = method.parameters.get(i)
            if (!param.isAnnotationPresent(PathParam) && !param.isAnnotationPresent(QueryParam) &&
                !param.isAnnotationPresent(HeaderParam) && !param.isAnnotationPresent(CookieParam)) {
                return args.get(i)
            }
        }
        return null
    }
}
